use std::error::Error;
use std::sync::{Arc, Mutex};
use simple_error::*;
use grapeTimerR::timer;
use std::time;
use log::*;

use crate::config::ActionConf;
use crate::action_http::new_http;
use crate::action_ping::new_ping;
use crate::action_tcp::new_tcp;
use crate::action_ws::new_websocket;
use crate::action_do::{executor_fail, executor_recover};


pub struct  ActionData {
    pub id:i32,
    pub name:String,
    pub task_action:ActionConf,
    pub fail_count:i32,
    pub fail_state:bool,
}

impl ActionData {
    pub fn do_failed(&mut self) {
        self.fail_state = true;
        self.fail_count+=1;

        let task_conf = &self.task_action;
        if self.fail_count >= task_conf.fail_count {
            executor_fail(task_conf);
        }
    }

    pub fn do_recover(&mut self) {
        if self.fail_state {
            self.fail_count = 0;
            self.fail_state = false;

            let task_conf = self.get_conf();
            executor_recover(task_conf);
        }
    }

    pub fn get_conf(&self) -> &ActionConf {
        return &self.task_action;
    }
}

pub trait ActionTrait : Send+Sync {
    fn id(&self) -> i32;
    fn name(&self) -> &str;
    fn append_data(&mut self,data:ActionData);
    fn executor(&mut self);

}

// 通过行为创建一个运行行为
pub fn new_action(task:&ActionConf) -> Result<Arc<Mutex<dyn ActionTrait>>,Box<dyn Error>> {
    let napp_data = ActionData{
        id: task.id,
        name: task.name.clone(),
        task_action: task.clone(),
        fail_count:0,
        fail_state:false,
    };

    info!("build task action:{} id:{} type:{}...",task.name,task.id,task.atype);

    let ntype = task.atype.clone();
    match ntype.as_str() {
        "http"=> {
            Ok(Arc::new(Mutex::new(new_http(napp_data))))
        }
        "ping" => {
            Ok(Arc::new(Mutex::new(new_ping(napp_data))))
        }
        "tcp" | "tcp_tls" => {
            Ok(Arc::new(Mutex::new(new_tcp(napp_data))))
        }
        "websocket" => {
            Ok(Arc::new(Mutex::new(new_websocket(napp_data))))
        }
        _ => {
            bail!("error action type...")
        }
    }
}

pub fn spawn_action(task:&ActionConf) -> Result<(),Box<dyn Error>> {
    let mut action_trait = new_action(task)?;

    info!("start task:{} id:{} type:{}...",task.name,task.id,task.atype);
    let r = timer::spawn_ticker(time::Duration::from_millis(task.tick as u64),
                                task.count,move |ixd| {
            action_trait.lock().unwrap().executor();
    });

    match r {
        Ok(x) => { Ok(()) }
        Err(e) => { bail!(e.to_string()) }
    }
}